#include "STabCtrl.h"

#include <core/ImageMgr.h>
#include <core/StyleMgr.h>
#include <qtooltip.h>

#include <QApplication>
#include <QColor>
#include <QDrag>
#include <QHBoxLayout>
#include <QMimeData>
#include <QRect>
#include <QStylePainter>

#include "QWindow"

STabLabel::STabLabel(const QString& t, QWidget* parent /*= nullptr*/)
    : QLabel(t, parent) {
  setAlignment(Qt::AlignCenter);
  setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
  setMouseTracking(true);
}

STabLabel::~STabLabel() {}

void STabLabel::setDragable(bool v) {
  m_canDrag = v;
  setAcceptDrops(m_canDrag);
}

void STabLabel::showSelBorder(bool show, QString clrFg) {
  if (show) {
    setStyleSheet(
        styleSheet() +
        QString("STabLabel[selected=\"true\"]	{background-color:#%1; "
                "color:#%3; border:1px solid #%2;border-radius:2px}")
            .arg(StyleMgr::instance().color("BackNormal").rgb(), 0, 16)
            .arg(StyleMgr::instance().color("TabSelBorder").rgb(), 0, 16)
            .arg(StyleMgr::instance().color(clrFg).rgb(), 0, 16));
  } else {
    setStyleSheet(styleSheet() +
                  QString("STabLabel[selected=\"true\"]	{color:#%1;}")
                      .arg(StyleMgr::instance().color(clrFg).rgb(), 0, 16));
  }
}

void STabLabel::mousePressEvent(QMouseEvent* event) {
  if (event->button() == Qt::LeftButton && m_canDrag) {
    m_dragPos = event->pos();
  }

  QLabel::mousePressEvent(event);
}

void STabLabel::mouseReleaseEvent(QMouseEvent* event) {
  if (event->button() == Qt::LeftButton && rect().contains(event->pos())) {
    emit clicked(m_index);
  }

  QLabel::mouseReleaseEvent(event);
}

void STabLabel::paintEvent(QPaintEvent* event) {
  QLabel::paintEvent(event);

  if (m_imgName != "") {
    QStylePainter painter(this);
    SImage* img = ImgMgr::instance().image(m_imgName, WidgetStatus::kNormal,
                                           devicePixelRatioF());

    QFontMetrics fm(font());
    int width = fm.horizontalAdvance(text());

    int x = (rect().center().x() + width / 2) + 2;
    int y = rect().center().y() - img->height() / 2;

    m_ImageRect.setRect(x, y, img->width(), img->height());

    if (img) painter.drawImage(m_ImageRect, *img);
  }
}

void STabLabel::mouseMoveEvent(QMouseEvent* event) {
  if (m_canDrag && (event->buttons().testFlag(Qt::LeftButton)) &&
      (event->pos() - m_dragPos).manhattanLength() >
          QApplication::startDragDistance()) {
    QDrag* drag = new QDrag(this);
    QMimeData* mimeData = new QMimeData();
    QByteArray itemData;
    QDataStream dataStream(&itemData, QIODevice::WriteOnly);
    dataStream << m_index;
    mimeData->setData("TabDrag", itemData);
    drag->setMimeData(mimeData);

    drag->exec(Qt::MoveAction);
  }

  if (m_ImageRect.contains(event->pos())) {
    QToolTip::showText(event->globalPos(), m_imgTip);
  } else {
    QToolTip::hideText();
  }
}

void STabLabel::dragEnterEvent(QDragEnterEvent* event) {
  if (!m_canDrag) return;

  if (event->mimeData()->hasFormat("TabDrag"))
    event->accept();
  else
    event->ignore();
}

void STabLabel::dropEvent(QDropEvent* event) {
  if (event->mimeData()->hasFormat("TabDrag")) {
    QByteArray itemData = event->mimeData()->data("TabDrag");
    QDataStream dataStream(&itemData, QIODevice::ReadOnly);
    int srcIndex;
    dataStream >> srcIndex;
    emit tabDragged(srcIndex, m_index);
    event->accept();
  } else
    event->ignore();
}

void STabLabel::dragMoveEvent(QDragMoveEvent* event) {
  QPoint globalPt = mapToGlobal(event->pos());
  QWindow* window = QGuiApplication::topLevelAt(globalPt);
  if (!window) return;

  // Hover
  QMouseEvent me(QEvent::MouseMove, globalPt, globalPt, Qt::NoButton,
                 Qt::NoButton, Qt::NoModifier);
  qApp->sendEvent(window, &me);
}

STabCtrl::STabCtrl(QWidget* p) : QFrame(p) {
  QHBoxLayout* l = new QHBoxLayout(this);
  l->setSpacing(2);
  l->setContentsMargins(0, 0, 0, 0);

  m_styleSheet =
      QString(
          "STabCtrl {border:1px solid #%6;border-radius:2px;}"
          "STabLabel						{color:#%1; "
          "background-color:#%2;border:none;}"
          "STabLabel:hover				{background-color:#%4;}"
          "STabLabel:disabled				{color:#%5; "
          "background:transparent;}"
          "STabLabel[TabPos=\"left\"]	{border-top-left-radius:2px; "
          "border-bottom-left-radius:2px; border-right:none;}"
          "STabLabel[TabPos=\"right\"]   {border-top-right-radius:2px; "
          "border-bottom-right-radius:2px; border-left:none;}"
          "STabLabel[selected=\"true\"]	{background-color:#%3;}")
          .arg(StyleMgr::instance().color("TextNormal").rgb(), 0, 16)
          .arg(StyleMgr::instance().color("TabBackNormal").rgb(), 0, 16)
          .arg(StyleMgr::instance().color("ButtonNormal").rgb(), 0, 16)
          .arg(StyleMgr::instance().color("Hover").rgb(), 0, 16)
          .arg(StyleMgr::instance().color("TxtDisabled").rgb(), 0, 16)
          .arg(StyleMgr::instance().color("TabBackNormal").rgb(), 0, 16);

  setStyleSheet(m_styleSheet);
}

void STabCtrl::addTab(const QString& name, const QString& tip) {
  STabLabel* item = new STabLabel(name, this);

  m_labels.push_back(item);
  if (!tip.isEmpty()) item->setToolTip(tip);
  item->setIndex(m_labels.size() - 1);
  layout()->addWidget(item);

  connect(item, &STabLabel::clicked, this, &STabCtrl::onTabClicked);
  connect(item, &STabLabel::tabDragged, this, &STabCtrl::onTabDragged);
}

void STabCtrl::setTabBkColor(int index, QColor color) {
  if (index >= (int)m_labels.size()) return;
  m_labels[index]->setBkColor(color);
}

void STabCtrl::setTabInfo(int index, const QString& imgName,
                          const QString& tip /*= ""*/) {
  m_labels[index]->setImgName(imgName);
  m_labels[index]->setImgTip(tip);
}

void STabCtrl::selectTab(int index) {
  // index为-1的时候，默认不选Tab
  if (index >= (int)m_labels.size() || m_labels.size() <= 0 ||
      index == m_selIndex || index < 0)
    return;
  m_lastSelIndex = m_selIndex;
  m_selIndex = index;
  for (int i = 0; i < (int)m_labels.size(); ++i) {
    m_labels[i]->setProperty("selected", (i == index) ? true : false);
  }

  QColor bkColor = StyleMgr::instance().color("ButtonNormal");
  if (m_labels[m_selIndex]->bkColor().isValid())
    bkColor = m_labels[m_selIndex]->bkColor();

  m_styleSheet =
      QString(
          "STabCtrl {border:1px solid #%6;border-radius:2px;}"
          "STabLabel						{color:#%1; "
          "background-color:#%2;border:none;}"
          "STabLabel:hover				{background-color:#%4;}"
          "STabLabel:disabled				{color:#%5; "
          "background:transparent;}"
          "STabLabel[TabPos = \"left\"]  "
          "{border-top-left-radius:2px;border-bottom-left-radius:2px;border-"
          "right:none;}"
          "STabLabel[TabPos = \"right\"] "
          "{border-top-right-radius:2px;border-bottom-right-radius:2px;border-"
          "left:none;}"
          "STabLabel[selected=\"true\"]	{background-color:#%3}")
          .arg(StyleMgr::instance().color("TextNormal").rgb(), 0, 16)
          .arg(StyleMgr::instance().color("TabBackNormal").rgb(), 0, 16)
          .arg(bkColor.rgb(), 0, 16)
          .arg(StyleMgr::instance().color("Hover").rgb(), 0, 16)
          .arg(StyleMgr::instance().color("TxtDisabled").rgb(), 0, 16)
          .arg(StyleMgr::instance().color("TabBackNormal").rgb(), 0, 16);

  setStyleSheet(m_styleSheet);
}

void STabCtrl::setTabDisabled(int index) {
  for (int i = 0; i < (int)m_labels.size(); ++i) {
    if (index == i) m_labels[i]->setDisabled(true);
  }
  setStyleSheet(m_styleSheet);
}

void STabCtrl::setTabText(int index, const QString& name) {
  if (index >= (int)m_labels.size() || index < 0) return;

  m_labels[index]->setText(name);
}

void STabCtrl::showSelBorder(int index, bool show, QString clrFg) {
  if (index >= (int)m_labels.size() || index < 0) return;

  m_labels[index]->showSelBorder(show, clrFg);
}

int STabCtrl::getTabCount() { return m_labels.size(); }

int STabCtrl::getSelTab() { return m_selIndex; }

int STabCtrl::getLastSelTab() { return m_lastSelIndex; }

void STabCtrl::setTabData(int index, QVariant v) {
  if (index >= (int)m_labels.size()) return;

  m_labels[index]->setProperty("data", v);
}

QVariant STabCtrl::tabData(int index) {
  if (index >= (int)m_labels.size()) return QVariant();

  return m_labels[index]->property("data");
}

void STabCtrl::setTabDragable(int index, bool v) {
  if (index < (int)m_labels.size()) m_labels[index]->setDragable(v);
}

void STabCtrl::onTabClicked(int index) {
  selectTab(index);
  emit tabSelected(index);
}

void STabCtrl::onTabDragged(int srcIndex, int destIndex) {
  STabLabel* src = m_labels[srcIndex];
  STabLabel* des = m_labels[destIndex];
  src->setIndex(destIndex);
  des->setIndex(srcIndex);
  std::swap(m_labels[srcIndex], m_labels[destIndex]);
  for (int i = 0; i < (int)m_labels.size(); ++i) {
    layout()->removeWidget(m_labels[i]);
  }
  for (int i = 0; i < (int)m_labels.size(); ++i) {
    layout()->addWidget(m_labels[i]);
  }

  int selIndex = m_selIndex;
  if (m_selIndex == srcIndex)
    selIndex = destIndex;
  else if (m_selIndex == destIndex)
    selIndex = srcIndex;

  unHoverLabel(
      destIndex);  // 此时mapToGlobal仍未更新，所以需要使用之前的控件计算
  emit tabSeqChanged();
  onTabClicked(selIndex);
}

void STabCtrl::showEvent(QShowEvent* event) {
  m_labels[0]->setProperty("TabPos", "left");
  m_labels[m_labels.size() - 1]->setProperty("TabPos", "right");

  setStyleSheet(m_styleSheet);

  QFrame::showEvent(event);
}

void STabCtrl::unHoverLabel(int index) {
  STabLabel* src = m_labels[index];
  QRect r = src->rect();

  QPoint cursorPt = QCursor::pos();
  QPoint globalPt = src->mapToGlobal(r.center());
  QWindow* window = QGuiApplication::topLevelAt(globalPt);
  if (!window) return;

  QMouseEvent* me = new QMouseEvent(QEvent::MouseMove, globalPt, globalPt,
                                    Qt::NoButton, Qt::NoButton, Qt::NoModifier);
  qApp->postEvent(window, me);

  QMouseEvent* me2 =
      new QMouseEvent(QEvent::MouseMove, cursorPt, cursorPt, Qt::NoButton,
                      Qt::NoButton, Qt::NoModifier);
  qApp->postEvent(window, me2);
}
